package com.ruoyi.quartz.task;

import com.ruoyi.slope.data.crack.domain.SlopeCrackData;
import com.ruoyi.slope.data.crack.service.impl.SlopeCrackDataServiceImpl;
import com.ruoyi.slope.data.fencenet.domain.SlopeFencenetData;
import com.ruoyi.slope.data.fencenet.service.impl.SlopeFencenetDataServiceImpl;
import com.ruoyi.slope.data.geoacoustic.domain.SlopeGeoacousticData;
import com.ruoyi.slope.data.geoacoustic.service.impl.SlopeGeoacousticDataServiceImpl;
import com.ruoyi.slope.data.weather.domain.SlopeWeatherData;
import com.ruoyi.slope.data.weather.service.impl.SlopeWeatherDataServiceImpl;
import com.ruoyi.slope.device.sensor.domain.SlopeSensorList;
import com.ruoyi.slope.device.sensor.service.impl.SlopeSensorListServiceImpl;
import com.ruoyi.slope.gateway.domain.SlopeGatewayInfo;
import com.ruoyi.slope.gateway.service.impl.SlopeGatewayInfoServiceImpl;
import com.ruoyi.slope.warning.info.domain.SlopeWarningInfo;
import com.ruoyi.slope.warning.info.service.impl.SlopeWarningInfoServiceImpl;
import com.ruoyi.slope.warning.threshold.domain.SlopeWarningThreshold;
import com.ruoyi.slope.warning.threshold.service.impl.SlopeWarningThresholdServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Date;
import java.util.function.Function;

/**
 * 2025/5/12 下午2:57
 */
@Component("AlarmTask")
public class AlarmTask {

    @Autowired
    SlopeCrackDataServiceImpl slopeCrackDataServiceImpl;
    @Autowired
    SlopeWarningInfoServiceImpl slopeWarningInfoServiceImpl;
    @Autowired
    SlopeSensorListServiceImpl slopeSensorListServiceImpl;
    @Autowired
    SlopeWarningThresholdServiceImpl slopeWarningThresholdServiceImpl;
    @Autowired
    private SlopeGeoacousticDataServiceImpl slopeGeoacousticDataServiceImpl;
    @Autowired
    private SlopeFencenetDataServiceImpl slopeFencenetDataServiceImpl;
    @Autowired
    private SlopeWeatherDataServiceImpl slopeWeatherDataServiceImpl;
    @Autowired
    private SlopeGatewayInfoServiceImpl slopeGatewayInfoServiceImpl;

    public void alarm(){

        SlopeWarningThreshold slopeWarningThreshold = new SlopeWarningThreshold();
        slopeWarningThreshold.setDeptId(100L);
        // 获取到了阈值
        List<SlopeWarningThreshold> list = slopeWarningThresholdServiceImpl.alarmSelectSlopeWarningThresholdList(slopeWarningThreshold);
        for(SlopeWarningThreshold l:list){
            SlopeSensorList slopeSensorList = new SlopeSensorList();
            List<SlopeSensorList> sensor = slopeSensorListServiceImpl.alarmSelectSlopeSensorListList(slopeSensorList);
            for(SlopeSensorList sn:sensor){
                if(sn.getMonitorType().equals(l.getMonitoringType())){
                    switch (sn.getMonitorType()){
                        case "2": // 裂缝
                            crack(sn.getDeviceSn(),l);
                            break;
                        case "7": // 支挡结构物倾斜
                            qingxie(sn.getDeviceSn(),l);
                            break;
                        case "15": // 加速度
                            acceleration(sn.getDeviceSn(),l);
                            break;
                        case "次声":
                            cisheng(sn.getDeviceSn(),l);
                            break;
                        case "11": // 降雨量
                            jiangyu(sn.getDeviceSn(),l);
                            break;
                        case "12": // 气温
                            break;
                        case "21": // 土温
                            break;
                        case "20": // 土湿
                            break;
                        case "18": // 风速
                            break;
                        case "19": // 风向
                            break;
                    }
                }

            }
        }
        slopeCrackDataServiceImpl.scanned();
        slopeFencenetDataServiceImpl.scanned();
        slopeWeatherDataServiceImpl.scanned();
        slopeGeoacousticDataServiceImpl.scanned();
    }

    private void crack(String deviceSn,SlopeWarningThreshold slopeWarningThreshold ){
        double changeRate = slopeWarningThreshold.getChangeRate();
        double cumulativeValue = slopeWarningThreshold.getCumulativeValue();

        SlopeCrackData crackData = new SlopeCrackData();
        crackData.setDeviceSn(deviceSn);
        List<SlopeCrackData> list = slopeCrackDataServiceImpl.selectWarningDataList(crackData);
        if(list.isEmpty()){
            return;
        }

        double sum = 0;
        for (int i = 1; i < list.size(); i++) {
            sum += list.get(i).getCrackChange();
        }

        long days = 0;

        if(list.size() == 1){
            days = 1;
        }else{
            days = calculateDaysBetween(list,SlopeCrackData::getUpdateTime);
        }
        if (days <= 0) {
            days = 1; // 避免除以 0
        }
        double rate = sum/days ;

        String warningInfo = buildWarningDoubleTable("裂缝变化", sum, rate);

        SlopeWarningInfo slopeWarningInfo = new SlopeWarningInfo();
        slopeWarningInfo.setStartTime(list.get(0).getUpdateTime());
        slopeWarningInfo.setEndTime(list.get(list.size()-1).getUpdateTime());
        slopeWarningInfo.setSlopeName(slopeWarningThreshold.getSlopeName());
        slopeWarningInfo.setPointNumber(slopeWarningThreshold.getPointNumber());
        slopeWarningInfo.setWarningTime(new Date());
        slopeWarningInfo.setWarningInfo(warningInfo);
        slopeWarningInfo.setWarningDisposal(0L);
        slopeWarningInfo.setDeptId(slopeWarningThreshold.getDeptId());

        slopeWarningInfo.setDeviceSn(deviceSn);
        slopeWarningInfo.setMonitorType("2");
        slopeWarningInfo.setCumulativeValue(sum);
        slopeWarningInfo.setChangeRate(rate);

        slopeWarningInfo.setWarningType(warningInfo);
        singleOrDoubleJudge(changeRate, cumulativeValue, sum, rate, slopeWarningInfo);

    }

    private void cisheng(String deviceSn,SlopeWarningThreshold slopeWarningThreshold ){
        double changeRate = slopeWarningThreshold.getChangeRate();
        double cumulativeValue = slopeWarningThreshold.getCumulativeValue();

        SlopeGeoacousticData geoData = new SlopeGeoacousticData();
        geoData.setDeviceSn(deviceSn);
        List<SlopeGeoacousticData> list = slopeGeoacousticDataServiceImpl.warningSelectSlopeGeoacousticDataList(geoData);
        if(list.isEmpty()){
            return;
        }

        double sum = 0;
        for(SlopeGeoacousticData l : list){
            sum += l.getEffectiveSoundPressure();
        }

        long days = 0;

        if(list.size() == 1){
            days = 1;
        }else{
            days = calculateDaysBetween(list,SlopeGeoacousticData::getUpdateTime);
        }
        if (days <= 0) {
            days = 1; // 避免除以 0
        }
        double rate = sum/days ;

        String warningInfo = buildWarningDoubleTable("地表有效次声", sum,  rate);

        SlopeWarningInfo slopeWarningInfo = new SlopeWarningInfo();
        slopeWarningInfo.setStartTime(list.get(0).getUpdateTime());
        slopeWarningInfo.setEndTime(list.get(list.size()-1).getUpdateTime());
        slopeWarningInfo.setSlopeName(slopeWarningThreshold.getSlopeName());
        slopeWarningInfo.setPointNumber(slopeWarningThreshold.getPointNumber());
        slopeWarningInfo.setWarningTime(new Date());
        slopeWarningInfo.setWarningInfo(warningInfo);
        slopeWarningInfo.setWarningDisposal(0L);
        slopeWarningInfo.setDeptId(slopeWarningThreshold.getDeptId());

        slopeWarningInfo.setDeviceSn(deviceSn);
        slopeWarningInfo.setMonitorType("次声");
        slopeWarningInfo.setCumulativeValue(sum);
        slopeWarningInfo.setChangeRate(rate);

        slopeWarningInfo.setWarningType(warningInfo);
        singleOrDoubleJudge(changeRate, cumulativeValue, sum, rate, slopeWarningInfo);

    }

    private void qingxie(String deviceSn,SlopeWarningThreshold slopeWarningThreshold ){
        double changeRate = slopeWarningThreshold.getChangeRate();

        SlopeFencenetData fencenetData = new SlopeFencenetData();
        fencenetData.setDeviceSn(deviceSn);
        List<SlopeFencenetData> list = slopeFencenetDataServiceImpl.warningSelectSlopeFencenetDataList(fencenetData);
        if(list.isEmpty()){
            return;
        }
        double xSum = 0;
        double ySum = 0;
        double zSum = 0;
        for(SlopeFencenetData l : list){
            xSum += l.getXTiltAngle();
            ySum += l.getYTiltAngle();
            zSum += l.getZTiltAngle();
        }

        String xWarningInfo = buildWarningSingleTable("倾斜物 X倾斜角", xSum);
        String yWarningInfo = buildWarningSingleTable("倾斜物 Y倾斜角", ySum);
        String zWarningInfo = buildWarningSingleTable("倾斜物 Z倾斜角", zSum);



        SlopeWarningInfo slopeWarningInfo = new SlopeWarningInfo();
        slopeWarningInfo.setStartTime(list.get(0).getUpdateTime());
        slopeWarningInfo.setEndTime(list.get(list.size()-1).getUpdateTime());
        slopeWarningInfo.setSlopeName(slopeWarningThreshold.getSlopeName());
        slopeWarningInfo.setPointNumber(slopeWarningThreshold.getPointNumber());
        slopeWarningInfo.setWarningTime(new Date());
        slopeWarningInfo.setWarningDisposal(0L);
        slopeWarningInfo.setDeptId(slopeWarningThreshold.getDeptId());

        slopeWarningInfo.setWarningType(xWarningInfo);
        slopeWarningInfo.setWarningInfo(xWarningInfo);
        slopeWarningInfo.setDeviceSn(deviceSn);
        slopeWarningInfo.setMonitorType("7");
        slopeWarningInfo.setCumulativeValue(xSum);
        singleJudge(changeRate, xSum, slopeWarningInfo);

        slopeWarningInfo.setWarningType(yWarningInfo);
        slopeWarningInfo.setWarningInfo(yWarningInfo);
        slopeWarningInfo.setCumulativeValue(ySum);
        singleJudge(changeRate, ySum, slopeWarningInfo);

        slopeWarningInfo.setWarningType(zWarningInfo);
        slopeWarningInfo.setWarningInfo(zWarningInfo);
        slopeWarningInfo.setCumulativeValue(zSum);
        singleJudge(changeRate, zSum, slopeWarningInfo);

    }

    private void acceleration(String deviceSn,SlopeWarningThreshold slopeWarningThreshold ){
        double changeRate = slopeWarningThreshold.getChangeRate();
        double cumulativeValue = slopeWarningThreshold.getCumulativeValue();

        SlopeCrackData crackData = new SlopeCrackData();
        crackData.setDeviceSn(deviceSn);
        List<SlopeCrackData> list = slopeCrackDataServiceImpl.selectWarningDataList(crackData);

        if(list.isEmpty()){
            return;
        }

        double xSum = 0;
        double ySum = 0;
        double zSum = 0;
        for(SlopeCrackData l : list){
            xSum += l.getXAcceleration();
            ySum += l.getYAcceleration();
            zSum += l.getZAcceleration();
        }
        long days = 0;

        if(list.size() == 1){
            days = 1;
        }else{
            days = calculateDaysBetween(list,SlopeCrackData::getUpdateTime);
        }
        if (days <= 0) {
            days = 1; // 避免除以 0
        }
        double xRate = xSum/days ;
        double yRate = ySum/days ;
        double zRate = zSum/days ;

        String xWarningInfo = buildWarningDoubleTable("加速度 X加速度", xSum,  xRate);
        String yWarningInfo = buildWarningDoubleTable("加速度 Y加速度", ySum,  yRate);
        String zWarningInfo = buildWarningDoubleTable("加速度 Z加速度", zSum,  zRate);

        SlopeWarningInfo slopeWarningInfo = new SlopeWarningInfo();
        slopeWarningInfo.setStartTime(list.get(0).getUpdateTime());
        slopeWarningInfo.setEndTime(list.get(list.size()-1).getUpdateTime());
        slopeWarningInfo.setSlopeName(slopeWarningThreshold.getSlopeName());
        slopeWarningInfo.setPointNumber(slopeWarningThreshold.getPointNumber());
        slopeWarningInfo.setWarningTime(new Date());
        slopeWarningInfo.setWarningDisposal(0L);
        slopeWarningInfo.setDeptId(slopeWarningThreshold.getDeptId());
        slopeWarningInfo.setDeviceSn(deviceSn);
        slopeWarningInfo.setMonitorType("15");


        slopeWarningInfo.setWarningType(xWarningInfo);
        slopeWarningInfo.setWarningInfo(xWarningInfo);
        slopeWarningInfo.setCumulativeValue(xSum);
        slopeWarningInfo.setChangeRate(xRate);
        singleOrDoubleJudge(changeRate, cumulativeValue, xSum, xRate, slopeWarningInfo);

        slopeWarningInfo.setWarningType(yWarningInfo);
        slopeWarningInfo.setWarningInfo(yWarningInfo);
        slopeWarningInfo.setCumulativeValue(ySum);
        slopeWarningInfo.setChangeRate(yRate);
        singleOrDoubleJudge(changeRate, cumulativeValue, ySum, yRate, slopeWarningInfo);

        slopeWarningInfo.setWarningType(zWarningInfo);
        slopeWarningInfo.setWarningInfo(zWarningInfo);
        slopeWarningInfo.setCumulativeValue(zSum);
        slopeWarningInfo.setChangeRate(zRate);
        singleOrDoubleJudge(changeRate, cumulativeValue, zSum, zRate, slopeWarningInfo);

    }

    private void jiangyu(String deviceSn,SlopeWarningThreshold slopeWarningThreshold ){
        double changeRate = slopeWarningThreshold.getChangeRate();
        double cumulativeValue = slopeWarningThreshold.getCumulativeValue();

        SlopeWeatherData weatherData = new SlopeWeatherData();
        weatherData.setDeviceSn(deviceSn);
        List<SlopeWeatherData> list = slopeWeatherDataServiceImpl.warningSelectSlopeWeatherDataList(weatherData);
        if(list.isEmpty()){
            return;
        }
        double sum = 0;
        for(SlopeWeatherData l : list){
            sum += l.getDailyRainfall();
        }
        long days = 0;

        if(list.size() == 1){
            days = 1;
        }else{
            days = calculateDaysBetween(list,SlopeWeatherData::getUpdateTime);
        }
        if (days <= 0) {
            days = 1; // 避免除以 0
        }
        double rate = sum/days ;

        String warningInfo = buildWarningSingleTable("日降雨量", sum);

        SlopeWarningInfo slopeWarningInfo = new SlopeWarningInfo();
        slopeWarningInfo.setStartTime(list.get(0).getUpdateTime());
        slopeWarningInfo.setEndTime(list.get(list.size()-1).getUpdateTime());
        slopeWarningInfo.setSlopeName(slopeWarningThreshold.getSlopeName());
        slopeWarningInfo.setPointNumber(slopeWarningThreshold.getPointNumber());
        slopeWarningInfo.setWarningTime(new Date());
        slopeWarningInfo.setWarningDisposal(0L);
        slopeWarningInfo.setDeptId(slopeWarningThreshold.getDeptId());
        slopeWarningInfo.setDeviceSn(deviceSn);
        slopeWarningInfo.setMonitorType("11");
        slopeWarningInfo.setCumulativeValue(sum);
        slopeWarningInfo.setChangeRate(rate);

        slopeWarningInfo.setWarningType(warningInfo);
        slopeWarningInfo.setWarningInfo(warningInfo);
        singleOrDoubleJudge(changeRate, cumulativeValue, sum, rate, slopeWarningInfo);
    }

    public static <T> long calculateDaysBetween(List<T> list, Function<T, Date> timeGetter) {
        if (list == null || list.size() < 2) {
            throw new IllegalArgumentException("List 至少需要两个元素");
        }

        Date startDate = timeGetter.apply(list.get(0));
        Date endDate = timeGetter.apply(list.get(list.size() - 1));

        LocalDate startLocalDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate endLocalDate = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

        return ChronoUnit.DAYS.between(startLocalDate, endLocalDate);
    }

    private void singleJudge(double changeRate, double sum, SlopeWarningInfo slopeWarningInfo) {
        if(sum>=changeRate){
            slopeWarningInfo.setWarningLevel("橙色单控预警");
        }else if(sum>=changeRate*0.85) {
            slopeWarningInfo.setWarningLevel("黄色单控预警");
        }else if(sum>=changeRate*0.75) {
            slopeWarningInfo.setWarningLevel("蓝色单控预警");
        }else{
            return;
        }
        slopeWarningInfo.setWarningInfo("边坡"+slopeWarningInfo.getSlopeName()+slopeWarningInfo.getWarningLevel()+"。\n"+slopeWarningInfo.getWarningInfo());
        slopeWarningInfoServiceImpl.insertSlopeWarningInfo(slopeWarningInfo);

        MessageSending.send(slopeWarningInfo);

        alarm(slopeWarningInfo);
    }

    private void singleOrDoubleJudge(double changeRate, double cumulativeValue, double sum, double cumulative, SlopeWarningInfo slopeWarningInfo) {
        if(sum>=cumulativeValue  && cumulative>=changeRate){
            slopeWarningInfo.setWarningLevel("红色双控预警");
        }else if(sum>=cumulativeValue || cumulative>=changeRate){
            slopeWarningInfo.setWarningLevel("橙色单控预警");
        }else if(sum >= cumulativeValue*0.85 && cumulative>=changeRate*0.85){
            slopeWarningInfo.setWarningLevel("橙色双控预警");
        }else if(sum>=cumulativeValue*0.85 || cumulative>=changeRate*0.85) {
            slopeWarningInfo.setWarningLevel("黄色单控预警");
        }else if(sum >= cumulativeValue*0.70 && cumulative>=changeRate*0.70){
            slopeWarningInfo.setWarningLevel("黄色双控预警");
        }else if(sum>=cumulativeValue*0.75 || cumulative>=changeRate*0.75) {
            slopeWarningInfo.setWarningLevel("蓝色单控预警");
        }else if(sum >= cumulativeValue*0.60 && cumulative>=changeRate*0.60){
            slopeWarningInfo.setWarningLevel("蓝色双控预警");
        }else{
            return;
        }
        slopeWarningInfo.setWarningInfo("边坡"+slopeWarningInfo.getSlopeName()+slopeWarningInfo.getWarningLevel()+"。\n"+slopeWarningInfo.getWarningInfo());
        slopeWarningInfoServiceImpl.insertSlopeWarningInfo(slopeWarningInfo);

        // MessageSending.send(slopeWarningInfo);

        alarm(slopeWarningInfo);
    }

    public static String buildWarningSingleTable(String axis, double sum) {
        return axis + "预警。累计值：" + String.format("%.3f", sum);
    }

    public static String buildWarningDoubleTable(String axis, double sum, double rate) {
        return axis + "预警。累计值：" + String.format("%.3f", sum) + "，变化率：" + String.format("%.3f", rate);
    }

    public void alarm(SlopeWarningInfo slopeWarningInfo){
        SlopeGatewayInfo slopeGatewayInfo = new SlopeGatewayInfo();
        slopeGatewayInfo.setDeptId(slopeWarningInfo.getDeptId());
        List<SlopeGatewayInfo> list = slopeGatewayInfoServiceImpl.selectSlopeGatewayInfoList(slopeGatewayInfo);
        if(list!=null && !list.isEmpty()){
            slopeGatewayInfoServiceImpl.alarm(list.get(0).getGatewaySn());
        }
    }

}
